package parserjc.jaccie;

import java.util.*;

/**
 * This State is a state of an automat of a scanner.
 * A State holds an vector of Transitions.
 * A state could be an intern state. That means the state
 * has more then one follow state, because the state has to
 * handle an iteration. The intern state decide dependent of his
 * loops counter which follow state he has to return.
 */
public class State extends Object {

String ls = "\n"; //System.getProperty("line.separator");

/**
 * The number of the state.
 */
private int stateNumber;

/**
 * The string representation of the state.
 */
private String stateText;

/**
 * Indicates if the state is final state.
 */
private boolean finalState = false;

/**
 * The possible state transitions.
 */
private Vector transitions = new Vector();

/**
 * Counter for iterations, like (.a|.b)[loops-loops2]
 */
private int loops = 0, loops2 = 0;

/**
 * The original loops, needed for reseting.
 */
private int loopsReset = 0, loops2Reset = 0;

/**
 * Indicates if a iteration has a endless symbol, like a[1-*]
 */
private boolean endlessLoop = false;

/**
 * The follow state after ending the first loops.
 * This means loops <= 0;
 */
private Integer loopFollowState = new Integer(0);

/**
 * The follow state after ending the second loops.
 * This means loops2 <= 0;
 */
private Integer loopFollowState2 = new Integer(0);

/**
 * The follow state when we did not care about loops.
 */
private Integer normalFollowState = new Integer(0);

/**
 * True if this state is an intern state. That means if we
 * change in this state, the state can give you the real follow
 * state dependent of the loops/2 counters. On this way we have
 * allways a deterministic scanner.
 */
private boolean internState = false;

/**
 * If the state is an intern state he has an iteration, like
 * @(.a|.b)@[1-5]. The "@" symbol are the borders of the iteration.
 * The positions inside the string gives the indexes of the borders.
 */
private int[] borders = new int[2];

    /**
     * Constructs a new state for a scanner-automat.
     * @param stateNumber the state number
     * @param stateText the string representation of the state
     * @param finalState indicates if the state is final
     */
    public State(int stateNumber, String stateText, boolean finalState) {
        this.stateText = stateText;
        this.finalState = finalState;
        this.stateNumber = stateNumber;
    }

    /**
     * Constructs a new state for a scanner-automat.
     * The state get the state number -1.
     * @param stateText the string representation of the state
     * @param finalState indicates if the state is final
     */
    public State(String stateText, boolean finalState) {
        this(-1,stateText,finalState);
    }

    /**
     * Resets the loops/2 with the loopsReset/loops2Reset.
     */
    public void reset() {
        loops = loopsReset;
        loops2 = loops2Reset;
    }

    /**
     * Reset the loops/2 if the own borders inside the test borders.
     * @param testBorders the borders to compare with the state borders.
     */
    public void resetInnerState(int testBorders[]) {
        if (internState) {
            if (testBorders[1] > borders[1] && testBorders[0] < borders[0])
                reset();
        }
    }

    /**
     * @return the borders of the state.
     */
    public int[] getBorders() {
        return borders;
    }

    /**
     * @return true if this state is a intern state
     */
    public boolean isInternState() {
        return this.internState;
    }

    /**
     * Sets this state to an intern state. The state fills
     * his borders with the indexes of "@" symbol of his
     * string representation.
     */
    public void setInternState(boolean type) {
        borders[0] = stateText.indexOf("@",0);
        borders[1] = stateText.indexOf("@",borders[0]+1);
        this.internState = type;
    }

    /**
     * Sets that this state contains an endless loop.
     */
    public void setEndlessLoop(boolean endlessLoop) {
        this.endlessLoop = endlessLoop;
    }

    /**
     * @returns true if this state contains an endless loop.
     */
    public boolean isEndlessLoop() {
        return this.endlessLoop;
    }

    /**
     * @return the follow state.
     */
    public Integer getFollowState() {
        if ( loops-- > 0) return loopFollowState;
        if (endlessLoop || loops2-- > 0) return loopFollowState2;
        return normalFollowState;
    }

    /**
     * Sets the follow State.
     * @param type is 0 for a loop follow state, 1 for a normal follow state
     *             and > 1 for a loop2 follow state.
     * @param followState the follow state
     */
    public void setFollowState(int type, int followState) {
        if (type == 0) loopFollowState = new Integer(followState);
        else if (type == 1) normalFollowState = new Integer(followState);
        else loopFollowState2 = new Integer(followState);
    }

    /**
     * @return true if the state contains the specified transition
     * @see Transition
     */
    public boolean contains(Transition transition) {
        Enumeration trans = transitions.elements();
        while (trans.hasMoreElements())
            if (((Transition)trans.nextElement()).equals(transition))
            return true;
        return false;
    }

    /**
     * Sets the string representation of this state.
     */
    public void setStateText(String stateText) {
        this.stateText = stateText;
    }

    /**
     * Adds a new transition to the state.
     * @see Transition
     */
    public void add(Transition transition) {
        transitions.addElement(transition);
    }

    /**
     * Sets the loops/2 for this state.
     */
    public void setLoops(int loops, int loops2) {
        this.loops = loops;
        this.loopsReset = loops;
        this.loops2 = loops2;
        this.loops2Reset = loops2;
    }

    /**
     * @return the string representation of this state.
     */
    public String getStateText() {
        return stateText;
    }

    /**
     * @return the transitions of this state.
     */
    public Vector getTransitions() {
        return transitions;
    }

    /**
     * Tests the specified symbol, if a transition knows it and
     * @return the follow state if the symbol is valid else null
     * @see Transition
     */
    public Integer testSymbol(Integer symbol) {
        for (int i=0; i<transitions.size(); i++) {
            Transition t = (Transition)transitions.elementAt(i);
            Integer followState = t.testSymbol(symbol);
            if (followState != null) return followState;
        }
        return null;
    }

    /**
     * @return true if this state is a final state.
     */
    public boolean isFinalState() {
        return finalState;
    }

    /**
     * Sets if this state is a final state.
     */
    public void setFinalState(boolean finalState) {
        this.finalState = finalState;
    }

    /**
     * Sets the state number.
     */
    public void setStateNumber(int stateNumber) {
        this.stateNumber = stateNumber;
    }

    /**
     * @return the state number
     */
    public int getStateNumber() {
        return stateNumber;
    }

    /**
     * @return a string needed for building a java application
     */
    public String toString() {
        String space = "          ";
        String string = space + "state= new State("+this.stateNumber+",";
        string += "\""+this.stateText+"\","+finalState+");"+ls;
        if (internState) {
            string += space +"state.setInternState(true);"+ls;
            string += space+"state.setLoops("+loopsReset+","+loops2Reset+");"+ls;
            if (endlessLoop) string += space +"state.setEndlessLoop(true);"+ls;
            string += space +"state.setFollowState(0,"+loopFollowState.toString()+");"+ls;
            if (!endlessLoop) {
                string += space +"state.setFollowState(1,"+normalFollowState.toString()+");"+ls;
                if (loops2Reset > 0)
                    string += space +"state.setFollowState(2,"+loopFollowState2.toString()+");"+ls;
            }
            else string += space +"state.setFollowState(2,"+loopFollowState2.toString()+");"+ls;
        }
        if (!transitions.isEmpty())
            string += transString(transitions.elements());
        return string.substring(0,string.length()-1);
    }

    /**
     * @return a string needed for building a java application
     */
    private String transString(Enumeration e) {
        String space = "          ";
        String content = new String();
        Hashtable stringBag = new Hashtable();
        while (e.hasMoreElements()) {
            Transition t = (Transition)e.nextElement();
            if (stringBag.containsKey(t.followString())) {
                content = (String)stringBag.get(t.followString());
                content += ","+t.toString();
                stringBag.put(t.followString(),content);
            }
            else stringBag.put(t.followString(),t.toString());
        }
        content = new String();
        Enumeration followStateStrings = stringBag.keys();
        while (followStateStrings.hasMoreElements()) {
            String key = (String)followStateStrings.nextElement();
            content += space +"state.add(new Transition(\"";
            content += (String)stringBag.get(key)+"\",";
            content += key+"));"+ls;
        }
        return content;
    }
}